﻿using System;
using eslib.DataRetrievalLib.Matchers;
using System.Diagnostics;
using eslib.nnp5.Matchers;

namespace eslib.nnp5.layers
{
    /// <summary>
    /// FF协议传输层对象
    /// </summary>
    public class TransportLayer : ITransportLayer
    {
        private MicroFFMatcher matcher;

        private SendControlerBase sender;

        private PkgIDProvider IDProvider;

        /// <summary>
        /// 连接器
        /// </summary>
        public Connector connector { get; private set; }


        /// <summary>
        /// 构造传输层
        /// </summary>
        /// <param name="connector">通讯层连接对象</param>
        /// <param name="mode">发送器模式，默认为需要ack</param>
        public TransportLayer(Connector connector, SenderMode mode = SenderMode.AckMode)
        {
            this.connector = connector;

            matcher = new MicroFFMatcher();
            matcher.matchEvent += Matcher_matchEvent;

            connector.DisconnectEvent += Connector_DisconnectEvent;
            connector.RecvEvent += Connector_RecvEvent;


            //发送模式
            switch (mode)
            {
                case SenderMode.AckMode:    //需要ack模式
                    sender = new SendControler(connector, this);
                    break;

                case SenderMode.NoAckMode:  //不需要ack模式
                    sender = new SendControlerNoAck(connector);
                    break;
            }


            //包ID分配器
            IDProvider = new PkgIDProvider();
        }



        /// <summary>
        /// 重置,包括重置Sender
        /// </summary>
        public void Reset()
        {
            matcher.reset();
            sender.ResetQueues();
        }



        #region 打包部分

        /// <summary>
        /// 发送数据,并使用CRC校验,data为原始数据
        /// </summary>
        /// <param name="data">原始数据</param>
        public void sendData(byte[] data)
        {
            sendData(data, true);
        }


        /// <summary>
        /// 发送数据,data为原始数据
        /// </summary>
        /// <param name="data">原始数据</param>
        /// <param name="useCRC">是否使用CRC校验,默认为false,TCP不需要CRC校验</param>
        public void sendData(byte[] data, bool useCRC = false)
        {
            TransferredPKG pkg = new TransferredPKG();

            //分配包ID            
            pkg.setPkgID(IDProvider.next());

            pkg.setCRCSign(useCRC);
            pkg.SetData(data);

            //发送
            sender.sendDataPkg(pkg);
        }

        #endregion




        private void Connector_RecvEvent(Connector connector, byte[] buffer, int len)
        {
            inStream(buffer);
        }


        private void Connector_DisconnectEvent(Connector connector, string message)
        {
            sender.stop();
        }



        #region 解包部分

        /// <summary>
        /// 是否开启收到数据包后自动回复ack,默认为true
        /// </summary>
        public bool EnableAutoReplyAck { get; set; } = true;


        /// <summary>
        /// 匹配器事件
        /// </summary>
        /// <param name="buf"></param>
        private void Matcher_matchEvent(byte[] buf)
        {
            //判断ACK包
            if (isACKPkg(buf))      //ACK包
            {
                try
                {
                    //还原数据包
                    ACKPkg ack = ACKPkg.RestoreACK(buf);
                    NewACKEvent?.Invoke(ack);
                }
                catch { }
            }
            else        //数据包
            {
                try
                {
                    //还原数据包
                    TransferredPKG pkg = TransferredPKG.CreateTransferredPKG(buf);

                    //FF数据包
                    NewDataPKGEvent?.Invoke(pkg);

                    //原始数据包
                    NewTransportLayerDataEvent?.Invoke(buf);

                    if (EnableAutoReplyAck)
                    {
                        //回送ACK                    
                        sender.sendACKPkg(pkg.getPkgID());
                    }
                }
                catch (TransportLayerException err)
                {
                    //重置
                    Reset();

                    //数据包异常,等待对方重发，因为包ID可能不准确，等待发送方超时
                    Trace.TraceError($"{GetType().FullName}:{err.Message}");
                }
            }
        }


        /// <summary>
        /// 原始数据传入(由硬件层使用)
        /// </summary>
        /// <param name="buf"></param>
        void inStream(byte[] buf)
        {
            matcher.inStream(buf);
        }




        /// <summary>
        /// 判断包是否ACK包
        /// </summary>
        /// <param name="pkg"></param>
        /// <returns></returns>
        private bool isACKPkg(byte[] pkg)
        {
            for (int i = 0; i < 4; i++)
            {
                if (pkg[i] != ACKPkg.ackSign[i]) return false;
            }

            return true;
        }


        /// <summary>
        /// 新数据包接收事件
        /// </summary>
        public event NewDataPKGEventHandler NewDataPKGEvent;

        /// <summary>
        /// 新原始数据包接收事件
        /// </summary>
        public event NewTransportLayerDataEventHandler NewTransportLayerDataEvent;


        /// <summary>
        /// 新ACK接收事件
        /// </summary>
        public event NewACKEventHandler NewACKEvent;


        #endregion



    }


    /// <summary>
    /// 收到新数据包事件处理委托
    /// </summary>
    /// <param name="pkg"></param>
    public delegate void NewDataPKGEventHandler(TransferredPKG pkg);


    /// <summary>
    /// 收到新ACK事件处理委托
    /// </summary>
    /// <param name="ack"></param>
    public delegate void NewACKEventHandler(ACKPkg ack);

}
